package org.xlp.auto.create;

import org.xlp.assertion.AssertUtils;
import org.xlp.auto.create.exception.AutoCreatorDataLoseException;
import org.xlp.auto.create.util.AutoCreatorUtils;
import org.xlp.utils.XLPArrayUtil;
import org.xlp.utils.XLPDateUtil;
import org.xlp.utils.XLPStringUtil;
import org.xlp.utils.io.XLPIOUtil;
import org.xlp.utils.io.path.XLPFilePathUtil;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Date;

/**
 * Description: entity, dao, service, controller自动创建抽象类
 * <br/>date: 2023/11/30 11:18
 *
 * @version 1.0
 * @author: xlp
 */
public abstract class AbstractAutoCreator implements IAutoCreator{
    /**
     * 自动创建文件放在的根目录
     */
    private String rootPath;

    /**
     * 根包名
     */
    private String rootPackageName;

    /**
     * 子包名
     */
    private String subPackageName;

    /**
     * 标记当根目录不存在时是否自动创建出指定的根目录，true：自动创建，false：不创建
     */
    private boolean mikRootPath = true;

    /**
     * 实体名称
     */
    private String entityName;

    /**
     * 父类名称
     */
    private String supperClassName;

    /**
     * 所需实现接口名称
     */
    private String[] interfaceNames;

    /**
     * 版本
     */
    private String version = "1.0";

    /**
     * 作者
     */
    private String author = "xlp";

    /**
     * java文件类类型
     */
    private ClassType classType = ClassType.CLASS;

    /**
     * 是否覆盖已存在的文件，false：不覆盖，true：覆盖，默认不覆盖
     */
    private boolean cover = false;

    public AbstractAutoCreator(){}

    /**
     * 构造函数
     * @param abstractAutoCreator
     * @throws NullPointerException 假如参数为null，则抛出该异常
     */
    public AbstractAutoCreator(AbstractAutoCreator abstractAutoCreator){
        AssertUtils.isNotNull(abstractAutoCreator, "abstractAutoCreator parameter must not be null!");
        AutoCreatorUtils.autoCreatorConvert(this, abstractAutoCreator);
    }

    public String getRootPath() {
        return rootPath;
    }

    /**
     * 设置根目录
     * @param rootPath
     * @throws NullPointerException 假如参数为null，则抛出该异常
     */
    public void setRootPath(String rootPath) {
        AssertUtils.isNotNull(rootPath, "rootPath parameter must be not null or empty!");
        this.rootPath = rootPath.trim();
    }

    public boolean isMikRootPath() {
        return mikRootPath;
    }

    public void setMikRootPath(boolean mikRootPath) {
        this.mikRootPath = mikRootPath;
    }

    public String getEntityName() {
        return entityName;
    }

    /**
     * 设置实体名称
     * @param entityName
     * @throws NullPointerException 假如参数为null，则抛出该异常
     */
    public void setEntityName(String entityName) {
        AssertUtils.isNotNull(entityName, "entityName parameter must be not null or empty!");
        this.entityName = entityName.trim();
    }

    public String getRootPackageName() {
        return rootPackageName;
    }

    public void setRootPackageName(String rootPackageName) {
        this.rootPackageName = rootPackageName;
    }

    public String getSubPackageName() {
        return subPackageName;
    }

    public void setSubPackageName(String subPackageName) {
        this.subPackageName = subPackageName;
    }

    public String getSupperClassName() {
        return supperClassName;
    }

    public void setSupperClassName(String supperClassName) {
        this.supperClassName = supperClassName;
    }

    public String[] getInterfaceNames() {
        return interfaceNames;
    }

    public void setInterfaceNames(String[] interfaceNames) {
        this.interfaceNames = interfaceNames;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public boolean isCover() {
        return cover;
    }

    public void setCover(boolean cover) {
        this.cover = cover;
    }

    @Override
    public ClassType getClassType() {
        return classType;
    }

    public void setClassType(ClassType classType) {
        this.classType = classType;
    }

    /**
     * Java文件导入包内容
     * @return
     */
    protected String makeImportContent() {
        return XLPStringUtil.EMPTY;
    }

    /**
     * 添加要实现的接口名称
     * @param interfaceName
     */
    public void addInterfaceName(String interfaceName){
        if (XLPStringUtil.isEmpty(interfaceName)) return;

        if (XLPArrayUtil.isEmpty(interfaceNames)){
            interfaceNames = new String[]{interfaceName};
        } else {
            int len = interfaceNames.length;
            interfaceNames = Arrays.copyOf(interfaceNames, len + 1);
            interfaceNames[len] = interfaceName;
        }
    }

    /**
     * java文件 类描述信息
     * @return
     */
    protected String makeDescriptionContent(){
        // 类描述
        return "/**\n" +
                " * Description: \n" +
                " * <br/>date: " + XLPDateUtil.dateToString(new Date(), "yyyy/MM/dd HH:mm") + "\n" +
                " *\n" +
                " * @version " + getVersion() + "\n" +
                " * @author  " + getAuthor() + "\n" +
                " */";
    }

    /**
     * java文件注解类容
     * @return
     */
    protected abstract String makeAnnotationContent();

    /**
     * java文件对应的类主体内容
     * @return
     */
    protected String makeBodyContent(){
        return "\n\n";
    }

    /**
     * 制作文件类容
     * @return
     * @throws AutoCreatorDataLoseException 假如所需数据缺少，则抛出该异常
     */
    protected String makeFileContent() throws AutoCreatorDataLoseException {
        StringBuilder sb = new StringBuilder();
        // 添加导入数据
        String packageName = StringUtils.getFullPackageName(this);
        if (!XLPStringUtil.isEmpty(packageName)) {
            sb.append("package ").append(packageName).append(";");
            sb.append("\n\n");
        }

        // 导入包
        sb.append(XLPStringUtil.nullToEmpty(makeImportContent())).append("\n");

        // 类描述
        sb.append(XLPStringUtil.nullToEmpty(makeDescriptionContent())).append("\n");

        // 类注解
        sb.append(XLPStringUtil.nullToEmpty(makeAnnotationContent()));

        boolean isInterface = getClassType() == ClassType.INTERFACE;

        // 类信息
        sb.append("public ");
        if (!isInterface){
            if (getClassType() == ClassType.ABSTRACT_CLASS){
                sb.append("abstract ");
            }
            sb.append("class ");
        } else {
            sb.append("interface ");
        }
        if (XLPStringUtil.isEmpty(getEntityName())){
            throw new AutoCreatorDataLoseException("实体名称未设置。");
        }
        sb.append(getFileName().replace(Constants.JAVA_FILE_SUFFIX, ""));
        if (!XLPStringUtil.isEmpty(getSupperClassName()) && !isInterface){
            sb.append(" extends ").append(getSupperClassName());
        }
        String[] interfaces = getInterfaceNames();
        String interfaceStr = XLPStringUtil.join(interfaces, "", "", ",", true, true);
        if (!XLPStringUtil.isEmpty(interfaceStr)){
            if (!isInterface){
                sb.append(" implements ");
            } else {
                sb.append(" extends ");
            }
            sb.append(interfaceStr);
        }
        sb.append(" {\n");

        //类body
        sb.append(XLPStringUtil.nullToEmpty(makeBodyContent()));

        sb.append("}");
        return sb.toString();
    }

    /**
     * 写文件
     *
     * @throws IOException
     * @throws AutoCreatorDataLoseException 假如所需数据缺少，则抛出该异常
     */
    @Override
    public void write() throws IOException, AutoCreatorDataLoseException {
        String rootPath = getRootPath();
        if (XLPStringUtil.isEmpty(rootPath)){
            throw new AutoCreatorDataLoseException("文件根路径未设置。");
        }
        File rootDir = new File(XLPFilePathUtil.normalize(rootPath));

        // 判断是否需要创建根目录
        if (!isMikRootPath() && !rootDir.exists()){
            throw new AutoCreatorDataLoseException("设置的文件根目录不存在！[rootPath=" + rootPath + "]");
        }
        createDir(rootDir);

        String childPath = StringUtils.dotToSplash(XLPStringUtil.emptyTrim(getRootPackageName()));
        childPath = XLPFilePathUtil.normalize(childPath);
        if (!XLPStringUtil.isEmpty(childPath)){
            rootDir = new File(rootDir, childPath);
        }
        String subPkg = getSubPackageName();
        if (!XLPStringUtil.isEmpty(subPkg)){
            childPath = StringUtils.dotToSplash(subPkg.trim());
            childPath = XLPFilePathUtil.normalize(childPath);
            assert childPath != null;
            rootDir = new File(rootDir, childPath);
        }
        // 判断是否需要
        createDir(rootDir);
        // 创建相应java文件
        File javaFile = new File(rootDir, getFileName());

        //判断是否覆盖已有的文件
        if (javaFile.exists() && !isCover()) return;

        // 数据写入文件
        BufferedWriter bWriter = null;
        try {
            bWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(javaFile), StandardCharsets.UTF_8));
            XLPIOUtil.write(makeFileContent(), bWriter);
        } finally {
            XLPIOUtil.closeWrite(bWriter);
        }
    }

    /**
     * 获取java文件名称
     * @return
     * @throws AutoCreatorDataLoseException 假如获取失败，则抛出该异常
     */
    protected abstract String getFileName() throws AutoCreatorDataLoseException;

    /**
     * @param dir
     * @throws AutoCreatorDataLoseException 假如创建失败则抛出，则抛出该异常
     */
    private void createDir(File dir) throws AutoCreatorDataLoseException {
        if (!dir.exists()){
            if(!dir.mkdirs()){
                throw new AutoCreatorDataLoseException("设置的文件根目录创建失败！[rootPath=" + rootPath + "]");
            }
        }
    }
}
